/*
 * Copyright 2010 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.websocket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Collections;

/**
 * Creates WebSocket connections.
 */
public class WebSocketConnector {

  private static final Charset UTF8 = Charset.forName("UTF-8");

  private WebSocketHandshake handshake;
  private String proxyHost;
  private int proxyPort;

  public WebSocketConnector(WebSocketHandshake handshake) {
    this(handshake, null, -1);
  }

  public WebSocketConnector(WebSocketHandshake handshake, String proxyHost,
      int proxyPort) {
    this.handshake = handshake;
    this.proxyHost = proxyHost;
    this.proxyPort = proxyPort;
  }

  public WebSocket connect(URI wsServer, String origin) throws IOException {
    String scheme = wsServer.getScheme();
    if (!"ws".equals(scheme) && !"wss".equals(scheme)) {
      throw new IOException("Invalid scheme '" + scheme + "'");
    }
    boolean secure = "wss".equals(scheme);
    String wsHost = wsServer.getHost();
    int wsPort = wsServer.getPort();
    if (wsPort < 0) {
      wsPort = secure ? 443 : 80;
    }
    // TODO(jat): DoS checks, such as limiting the number of simultaneous
    // connection attempts
    String connectHost;
    int connectPort;
    if (proxyHost != null) {
      connectHost = proxyHost;
      connectPort = proxyPort;
    } else {
      connectPort = wsPort;
      connectHost = wsHost;
    }
    Socket sock = new Socket(connectHost, connectPort);
    sock.setKeepAlive(true);
    sock.setTcpNoDelay(true);
    InputStream fromServer = sock.getInputStream();
    OutputStream toServer = sock.getOutputStream();
    if (proxyHost != null) {
      
      String connectString = "CONNECT " + wsHost + ":" + wsPort
          + " HTTP/1.0\r\n\r\n";
      toServer.write(connectString.getBytes(UTF8));
      toServer.flush();
      String line = Util.readUtf8Line(fromServer);
      String[] parts = line.split(" ");
      if (parts.length < 3 || !"HTTP/1.0".equals(parts[0])
          || !"200".equals(parts[1])) {
        throw new IOException("Invalid proxy response '" + line + "'");
      }
      // read and discard headers
      Util.readHeaderFields(fromServer);
    }
    if ("wss".equals(scheme)) {
      // TODO(jat): TLS support
      throw new IOException("wss not supported");
    }
    return handshake.clientHandshake(fromServer, toServer,
        Collections.<Extension>emptySet(), wsServer, origin);
  }
}
